Skip to content

fix(agent): centralize Subagent capability policy#1982

Merged
yyhhyyyyyy merged 10 commits into
devfrom
fix/subagent-capability-policy
Jul 16, 2026
Merged

fix(agent): centralize Subagent capability policy#1982
yyhhyyyyyy merged 10 commits into
devfrom
fix/subagent-capability-policy

Conversation

@yyhhyyyyyy

@yyhhyyyyyy yyhhyyyyyy commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Centralize Subagent availability around the DeepChat Agent policy and remove the conflicting Session-level toggle.

The Agent configuration is now the single persisted policy source. Subagents are enabled by default for the built-in DeepChat Agent, while the model decides whether delegation is appropriate for each request.

Problem

Subagent availability previously depended on several independent states:

  • Agent-level subagentEnabled
  • Session-level subagent_enabled
  • available Subagent slots
  • generic disabled-tool settings
  • a tool catalog cache that did not fully track capability changes

These states could disagree, causing the UI to show Subagents as enabled while the model did not receive subagent_orchestrator.

Changes

  • Add a typed, centralized Subagent capability snapshot.
  • Default the built-in DeepChat Agent to enabled with Explorer, Implementer, and Reviewer slots.
  • Migrate legacy Agent configurations safely and idempotently.
  • Keep custom Agent opt-outs unchanged.
  • Remove the Session, Draft, New Thread, and composer Subagent toggles.
  • Keep the legacy new_sessions.subagent_enabled column for database compatibility while no longer using it for authorization.
  • Refresh tool catalogs when Agent policy or slot definitions change.
  • Revalidate current policy before admitting a new Subagent run.
  • Prevent Subagent child Sessions from recursively delegating.
  • Reserve subagent_orchestrator against MCP tool shadowing.
  • Remove generic disabled-tool control from the dedicated orchestrator capability.
  • Preserve active runs when policy changes; the new policy applies to future admissions.
  • Add conservative delegation guidance covering latency, token cost, task isolation, and overlapping writes.
  • Keep configless custom Agents independent from the built-in Agent’s opt-out.
  • Fail closed when persisted Agent configuration is unreadable.
  • Prevent non-DeepChat compatibility Sessions from receiving the orchestrator definition.
  • Keep MCP/Skill extension policy available when Agent-kind lookup fails while closing only the Subagent capability.

Summary by CodeRabbit

  • New Features
    • Subagent delegation is now controlled through DeepChat Agent Settings (slot-based).
    • Enabled agents restore default delegation slots and are fail-closed if configuration results in no valid slots.
  • User Experience
    • Removed session-level subagent toggles from chat controls and session creation; subagent availability is driven by the current agent policy and session type.
    • Updated delegation guidance and localized UI text to explain delegation behavior and added resource usage.
  • Documentation
    • Added and updated architecture specs, including the Subagent Capability Policy plan, rules, and validation criteria.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36c87767-7b4c-4ae0-8005-b0ab1fc4f477

📥 Commits

Reviewing files that changed from the base of the PR and between adced44 and 668a9d4.

📒 Files selected for processing (4)
  • src/main/presenter/index.ts
  • src/shared/lib/deepchatSubagents.ts
  • test/main/presenter/index.test.ts
  • test/main/presenter/sessionApplication/runtimeIntegration.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/shared/lib/deepchatSubagents.ts

📝 Walkthrough

Walkthrough

This PR moves subagent availability to Agent-level capability resolution with normalized slots, cache keys, migration, and fail-closed behavior. It removes Session-level subagentEnabled state and controls, preserves the legacy database column, updates orchestrator exposure, and adds architecture, UI, integration, and contract coverage.

Changes

Subagent capability policy

Layer / File(s) Summary
Policy contracts and architecture
docs/architecture/subagent-capability-policy/*, docs/architecture/session-management.md, docs/architecture/tool-system.md, src/shared/types/*, src/shared/lib/*
Defines capability snapshots, failure reasons, cache-key requirements, Agent-level policy, child-session restrictions, migration rules, reserved orchestrator exposure, and delegation guidance.
Agent policy storage and migration
src/main/agent/deepchat/*, src/main/presenter/configPresenter/*, test/main/agent/deepchat/*, test/main/presenter/configPresenter/*
Normalizes and validates DeepChat configuration, resolves missing or unreadable configs, applies migration version 3 defaults, and tests retry and invariant behavior.
Session and persistence boundary
src/main/agent/shared/*, src/main/presenter/sessionApplication/*, src/main/presenter/sqlitePresenter/*, src/main/routes/*, src/shared/contracts/*
Removes subagentEnabled from session creation, updates, schemas, assignments, routes, and database writes while retaining the legacy column at its database-owned value.
Runtime capability and tool exposure
src/main/presenter/agentRuntimePresenter/*, src/main/presenter/toolPresenter/*, src/main/presenter/index.ts
Passes capability snapshots through tool profiles, refreshes cache fingerprints, exposes subagent_orchestrator as a system-model capability, reserves its name, and revalidates before admission.
Agent settings and renderer retirement
src/renderer/settings/*, src/renderer/src/components/*, src/renderer/src/pages/*, src/renderer/src/stores/*, src/renderer/src/i18n/*
Restores default slots when enabling an empty policy, protects the final enabled slot, removes Session-level toggles and draft state, and updates localized delegation guidance.
Validation coverage
test/main/*, test/renderer/*
Adds coverage for repository defaults and invariants, migration retry behavior, capability cache keys, tool exposure and revalidation, legacy-column preservation, route removal, integration flow, and renderer behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: centralizing subagent capability policy and removing session-level toggle behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/subagent-capability-policy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
test/main/presenter/sessionApplication/runtimeIntegration.test.ts (1)

814-815: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the resolved configuration is applied.

These assertions only prove that the resolver methods were called; a regression that calls them and ignores the returned subagent configuration would still pass. Add an assertion against the created session’s capability or exposed tool state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/presenter/sessionApplication/runtimeIntegration.test.ts` around
lines 814 - 815, Strengthen the session creation test around
configPresenter.getAgentType and resolveDeepChatAgentConfig by asserting that
the resolved subagent configuration is applied to the created session. Verify
the session’s capability or exposed tool state reflects the resolved
configuration, rather than only checking that the resolver methods were invoked.
src/shared/lib/deepchatSubagents.ts (2)

194-202: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Remove redundant slot normalization.

normalizeDeepChatSubagentConfig already normalizes the subagents array internally. You can check its length directly instead of unnecessarily passing it through normalizeDeepChatSubagentSlots a second time.

♻️ Proposed refactor
 export const assertDeepChatSubagentConfigInvariant = (config: DeepChatAgentConfig): void => {
   const normalized = normalizeDeepChatSubagentConfig(config)
   if (
     normalized.subagentEnabled !== false &&
-    normalizeDeepChatSubagentSlots(normalized.subagents).length === 0
+    (!normalized.subagents || normalized.subagents.length === 0)
   ) {
     throw new Error('Enabled DeepChat Subagents require at least one valid slot.')
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/lib/deepchatSubagents.ts` around lines 194 - 202, In
assertDeepChatSubagentConfigInvariant, remove the second
normalizeDeepChatSubagentSlots call and check the length of normalized.subagents
directly, preserving the existing enabled-state condition and error behavior.

169-171: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Remove redundant array mapping.

normalizeDeepChatSubagentSlots already returns a freshly allocated array populated with newly created slot objects. The .map((slot) => ({ ...slot })) step is unnecessary because Array.prototype.sort() only reorders the array in place and does not mutate the objects themselves.

♻️ Proposed refactor
-  const slots = normalizeDeepChatSubagentSlots(input.slots)
-    .map((slot) => ({ ...slot }))
-    .sort(compareSubagentSlots)
+  const slots = normalizeDeepChatSubagentSlots(input.slots).sort(compareSubagentSlots)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/lib/deepchatSubagents.ts` around lines 169 - 171, Remove the
redundant `.map((slot) => ({ ...slot }))` from the
`normalizeDeepChatSubagentSlots(input.slots)` pipeline before
`sort(compareSubagentSlots)`. Keep sorting the freshly normalized array with
`compareSubagentSlots`, relying on `normalizeDeepChatSubagentSlots` for array
and slot-object allocation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/presenter/agentRuntimePresenter/toolResolver.ts`:
- Around line 243-252: Fail closed when deepchat agent configuration cannot be
read: in src/main/presenter/agentRuntimePresenter/toolResolver.ts lines 243-252,
update the rejection branch around resolveCapability to return unavailable
subagent capability rather than deriving it from null; in
src/main/presenter/index.ts lines 332-358, handle rejected
resolveDeepChatAgentConfig within resolveConversationSessionInfo using the same
safe fallback so the error does not bubble as an unhandled failure.

---

Nitpick comments:
In `@src/shared/lib/deepchatSubagents.ts`:
- Around line 194-202: In assertDeepChatSubagentConfigInvariant, remove the
second normalizeDeepChatSubagentSlots call and check the length of
normalized.subagents directly, preserving the existing enabled-state condition
and error behavior.
- Around line 169-171: Remove the redundant `.map((slot) => ({ ...slot }))` from
the `normalizeDeepChatSubagentSlots(input.slots)` pipeline before
`sort(compareSubagentSlots)`. Keep sorting the freshly normalized array with
`compareSubagentSlots`, relying on `normalizeDeepChatSubagentSlots` for array
and slot-object allocation.

In `@test/main/presenter/sessionApplication/runtimeIntegration.test.ts`:
- Around line 814-815: Strengthen the session creation test around
configPresenter.getAgentType and resolveDeepChatAgentConfig by asserting that
the resolved subagent configuration is applied to the created session. Verify
the session’s capability or exposed tool state reflects the resolved
configuration, rather than only checking that the resolver methods were invoked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a4f976b5-41a3-486c-97cc-7410ae7c95c6

📥 Commits

Reviewing files that changed from the base of the PR and between 59f3465 and adced44.

📒 Files selected for processing (91)
  • docs/architecture/session-application-coordinators/spec.md
  • docs/architecture/session-management.md
  • docs/architecture/subagent-capability-policy/plan.md
  • docs/architecture/subagent-capability-policy/spec.md
  • docs/architecture/subagent-capability-policy/tasks.md
  • docs/architecture/tool-system.md
  • src/main/agent/deepchat/deepChatAgentRepository.ts
  • src/main/agent/shared/appSessionService.ts
  • src/main/presenter/agentRuntimePresenter/toolResolver.ts
  • src/main/presenter/configPresenter/index.ts
  • src/main/presenter/configPresenter/systemPromptHelper.ts
  • src/main/presenter/cronJobs/runSessionStarter.ts
  • src/main/presenter/index.ts
  • src/main/presenter/sessionApplication/agentAssignmentCoordinator.ts
  • src/main/presenter/sessionApplication/agentAssignmentPolicy.ts
  • src/main/presenter/sessionApplication/lifecycleCoordinator.ts
  • src/main/presenter/sessionApplication/ports.ts
  • src/main/presenter/sqlitePresenter/tables/newSessions.ts
  • src/main/presenter/toolPresenter/agentTools/agentToolManager.ts
  • src/main/presenter/toolPresenter/agentTools/subagentOrchestratorTool.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/toolPresenter/runtimePorts.ts
  • src/main/routes/debug/createMockChatSession.ts
  • src/main/routes/index.ts
  • src/renderer/api/SessionClient.ts
  • src/renderer/settings/components/DeepChatAgentsSettings.vue
  • src/renderer/src/components/chat-input/McpIndicator.vue
  • src/renderer/src/components/chat/ChatStatusBar.vue
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/pages/NewThreadPage.vue
  • src/renderer/src/stores/ui/draft.ts
  • src/renderer/src/stores/ui/session.ts
  • src/shared/agentTools.ts
  • src/shared/contracts/common.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/sessions.routes.ts
  • src/shared/lib/deepchatSubagents.ts
  • src/shared/types/agent-interface.d.ts
  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/agent/deepchat/deepChatAgentRepository.test.ts
  • test/main/agent/shared/appSessionService.test.ts
  • test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts
  • test/main/presenter/agentRuntimePresenter/toolResolver.test.ts
  • test/main/presenter/configPresenter/deprecatedProviderCleanup.test.ts
  • test/main/presenter/configPresenter/systemPromptHelper.test.ts
  • test/main/presenter/cronJobRunSessionStarter.test.ts
  • test/main/presenter/exporter/agentSessionExporter.test.ts
  • test/main/presenter/remoteControlPresenter/remoteConversationRunner.test.ts
  • test/main/presenter/sessionApplication/agentAssignmentCoordinator.test.ts
  • test/main/presenter/sessionApplication/agentAssignmentPolicy.test.ts
  • test/main/presenter/sessionApplication/lifecycleCoordinator.test.ts
  • test/main/presenter/sessionApplication/lifecycleDeletionTransaction.test.ts
  • test/main/presenter/sessionApplication/projectionCoordinator.test.ts
  • test/main/presenter/sessionApplication/runtimeIntegration.test.ts
  • test/main/presenter/sessionApplication/sessionApplication.integration.test.ts
  • test/main/presenter/sessionApplication/turnCoordinator.test.ts
  • test/main/presenter/sqlitePresenter/newSessionsTable.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentMemoryTools.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentTapeTools.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentToolManagerSettings.test.ts
  • test/main/presenter/toolPresenter/agentTools/subagentOrchestratorTool.test.ts
  • test/main/presenter/toolPresenter/toolPresenter.test.ts
  • test/main/routes/chatService.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/shared/deepchatSubagents.test.ts
  • test/renderer/components/ChatStatusBar.test.ts
  • test/renderer/components/DeepChatAgentsSettings.test.ts
  • test/renderer/components/McpIndicator.test.ts
  • test/renderer/components/NewThreadPage.onboarding.test.ts
  • test/renderer/stores/memoryActivityStore.test.ts
  • test/renderer/stores/sessionStore.test.ts
💤 Files with no reviewable changes (22)
  • test/main/presenter/remoteControlPresenter/remoteConversationRunner.test.ts
  • test/main/presenter/exporter/agentSessionExporter.test.ts
  • test/main/presenter/sessionApplication/turnCoordinator.test.ts
  • test/main/routes/chatService.test.ts
  • test/main/presenter/sessionApplication/lifecycleDeletionTransaction.test.ts
  • test/main/presenter/sessionApplication/projectionCoordinator.test.ts
  • test/renderer/components/NewThreadPage.onboarding.test.ts
  • src/main/presenter/cronJobs/runSessionStarter.ts
  • test/renderer/stores/memoryActivityStore.test.ts
  • src/main/routes/index.ts
  • test/main/presenter/cronJobRunSessionStarter.test.ts
  • src/main/agent/shared/appSessionService.ts
  • src/renderer/src/stores/ui/draft.ts
  • test/renderer/stores/sessionStore.test.ts
  • src/shared/contracts/routes/sessions.routes.ts
  • test/main/routes/dispatcher.test.ts
  • src/renderer/src/pages/NewThreadPage.vue
  • src/renderer/src/components/chat/ChatStatusBar.vue
  • src/renderer/src/stores/ui/session.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/common.ts
  • src/renderer/api/SessionClient.ts

Comment thread src/main/presenter/agentRuntimePresenter/toolResolver.ts
@yyhhyyyyyy
yyhhyyyyyy merged commit cea493a into dev Jul 16, 2026
4 checks passed
@zhangmo8
zhangmo8 deleted the fix/subagent-capability-policy branch July 16, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant